#include "Rainr.Sockets.h"

namespace Rainr
{
    ClientSocket::ClientSocket()
    {
        this->Resets();
        this->handle = new Socket();
    }
    ClientSocket::ClientSocket(const SOCKADDR * address)
    {
        this->Resets();
        this->handle = new Socket(address);
    }
    ClientSocket::ClientSocket(const SOCKET * handle,const SOCKADDR * address)
    {
        this->Resets();
        this->handle = new Socket(handle,address);
    }
    ClientSocket::ClientSocket(const Socket & handle,ServerSocket * owner)
    {
        this->Resets();
        this->owner = owner;
        this->handle = new Socket(handle);
    }
    ClientSocket:: ClientSocket(const char * address,unsigned short hostPort,unsigned short addressFamily)
    {
        this->Resets();
        this->handle = new Socket(address,hostPort,addressFamily);
    }
    ClientSocket::~ClientSocket()
    {
    }

    DWORD WINAPI ClientSocket::AsyncClosedProc(LPVOID parameter)
    {
        ClosedInArgs * args = (ClosedInArgs*)parameter;
        ClosedOutArgs outArgs = ClosedOutArgs();
        outArgs.Socket = args->Socket;
        outArgs.Parameter = args->Parameter;
        OnClosedCallback callback = (OnClosedCallback) args->Callback;
        delete args;
        if(nullptr!= outArgs.Socket)
        {
            outArgs.Result= ((ClientSocket *)outArgs.Socket)->Close();
            if(!outArgs.Result)
            {
                outArgs.ErrorCode= WSAGetLastError();
            }
        }
        if(nullptr!= callback)
        {
            callback(outArgs);
        }
        return 0;
    }
    DWORD WINAPI ClientSocket::AsyncSendProc(LPVOID parameter)
    {
        SendInArgs * inArgs = (SendInArgs *)parameter;
        SendOutArgs outArgs = SendOutArgs();
        outArgs.PlanLength = inArgs->Length;
        outArgs.Socket = inArgs->Socket;
        outArgs.Parameter = inArgs->Parameter;
        OnSendCallback callback = (OnSendCallback)inArgs->Callback;
        outArgs.ResultLength = ((ClientSocket *)inArgs->Socket)->Send(inArgs->Buffer,inArgs->Length);
        if(SOCKET_ERROR == outArgs.ResultLength)
        {
            outArgs.ErrorCode = WSAGetLastError();
        }
        if(nullptr!=  inArgs->Buffer)
        {
            delete inArgs->Buffer;
        }
        delete inArgs;
        if(nullptr!= callback)
        {
            callback(outArgs);
        }
        return 0;
    }
    DWORD WINAPI ClientSocket::AsyncSendToProc(LPVOID parameter)
    {
        SendInArgs * inArgs = (SendInArgs *)parameter;
        SendOutArgs outArgs = SendOutArgs();
        outArgs.PlanLength = inArgs->Length;
        outArgs.Socket = inArgs->Socket;
        outArgs.Parameter = inArgs->Parameter;
        OnSendCallback callback = (OnSendCallback)inArgs->Callback;
        if(nullptr== inArgs->Address)
        {
            outArgs.ResultLength = ((ClientSocket *)inArgs->Socket)->SendTo(inArgs->Buffer,inArgs->Length);
        }
        else
        {
            outArgs.ResultLength = ((ClientSocket *)inArgs->Socket)->SendTo(inArgs->Address,inArgs->Buffer,inArgs->Length);
            delete inArgs->Address;
        }
        if(SOCKET_ERROR == outArgs.ResultLength)
        {
            outArgs.ErrorCode = WSAGetLastError();
        }
        if(nullptr!=  inArgs->Buffer)
        {
            delete inArgs->Buffer;
        }
        delete inArgs;
        if(nullptr!= callback)
        {
            callback(outArgs);
        }
        return 0;
    }
    DWORD WINAPI ClientSocket::AsyncRecvProc(LPVOID parameter)
    {
        RecvInArgs * inArgs = (RecvInArgs*)parameter;
        RecvOutArgs outArgs = RecvOutArgs();
        outArgs.Socket = inArgs->Socket;
        outArgs.Parameter = inArgs->Parameter;
        outArgs.PlanLength = inArgs->Length;
        outArgs.ResultData = new char[inArgs->Length];
        OnRecvCallback callback = (OnRecvCallback) inArgs->Callback;
        delete inArgs;
        outArgs.ResultLength = ((ClientSocket*)outArgs.Socket)->Recv(outArgs.ResultData,outArgs.PlanLength);
        if(SOCKET_ERROR == outArgs.ResultLength)
        {
            outArgs.ErrorCode = WSAGetLastError();
        }
        if(nullptr!= callback)
        {
            callback(outArgs);
        }
        delete outArgs.ResultData;
        return 0;
    }
    DWORD WINAPI ClientSocket::AsyncRepeatRecvProc(LPVOID parameter)
    {
        RecvInArgs * pInArgs = (RecvInArgs*)parameter;
        RecvInArgs inArgs = RecvInArgs();
        inArgs.Socket = pInArgs->Socket;
        inArgs.Length = pInArgs->Length;
        inArgs.Callback = pInArgs->Callback;
        inArgs.Parameter = pInArgs->Parameter;
        delete pInArgs;
        bool isRepeat = false;
        void * repeatTag = nullptr;
        do
        {
            if(nullptr== inArgs.Callback)
            {
                ((ClientSocket*)inArgs.Socket)->recvThread->Exit();break;
            }
            RecvOutArgs outArgs = RecvOutArgs();
            outArgs.Socket = inArgs.Socket;
            outArgs.Parameter = inArgs.Parameter;
            outArgs.PlanLength = inArgs.Length;
            outArgs.ResultData = new char[inArgs.Length];
            outArgs.ResultLength = ((ClientSocket*)outArgs.Socket)->Recv(outArgs.ResultData,outArgs.PlanLength);
            if(SOCKET_ERROR == outArgs.ResultLength)
            {
                outArgs.ErrorCode = WSAGetLastError();
            }
            isRepeat  =((OnRepeatRecvCallback)inArgs.Callback)(outArgs,inArgs.Length,repeatTag);
            delete outArgs.ResultData;
            if(!isRepeat)
            {
                ((ClientSocket*)inArgs.Socket)->recvThread->Exit();
            }
        }while(isRepeat);
        return 0;
    }
    DWORD WINAPI  ClientSocket::AsyncConnectProc(LPVOID parameter)
    {
        ConnectInArgs * inArgs = (ConnectInArgs *)parameter;
        ConnectOutArgs outArgs = ConnectOutArgs();
        outArgs.Socket = inArgs->Socket;
        outArgs.Parameter = inArgs->Parameter;
        OnConnectedCallback callback = (OnConnectedCallback)inArgs->Callback;
        if(nullptr== inArgs->Address)
        {
            outArgs.Result = ((ClientSocket *)inArgs->Socket)->Connect();
        }
        else
        {
            outArgs.Result = ((ClientSocket *)inArgs->Socket)->Connect(inArgs->Address);
            delete inArgs->Address;
        }
        if(!outArgs.Result)
        {
            outArgs.ErrorCode = WSAGetLastError();
        }
        delete  inArgs;
        if(nullptr!= callback)
        {
            callback(outArgs);
        }
        return 0;
    }

    void ClientSocket::Resets()
    {
        this->owner= nullptr;
        this->handle = nullptr;
        this->recvThread=nullptr;
        this->OnSend = nullptr;
        this->OnRecv = nullptr;
        this->OnConnected = nullptr;
        this-> OnClosedParameter = nullptr;
        this-> OnSendParameter = nullptr;
        this->OnRecvParameter = nullptr;
        this->OnRepeatRecvParameter = nullptr;
        this->OnConnectedParameter = nullptr;
    }

    bool ClientSocket::IsValid()
    {
        if(nullptr== this->handle)
        {
            return false;
        }
        return this->handle->IsValid();
    }

    Socket * ClientSocket::GetSocket()
    {
        return this->handle;
    }

    ServerSocket * ClientSocket::GetOwner()
    {
        return this->owner;
    }

    bool ClientSocket::Create(int addressFamily ,int socketType, int protocolType)
    {
        if(nullptr== this->handle)
        {
            return false;
        }
        return this->handle->Create(addressFamily,socketType,protocolType);
    }

    bool ClientSocket::Close()
    {
        if(nullptr!= this->handle)
        {
            if(!this->handle->Close())
            {
                return false;
            }
            delete this->handle;
            this->handle = nullptr;
        }
        if(nullptr!= this->recvThread)
        {
            if(!this->recvThread->Close())
            {
                return false;
            }
            delete this->recvThread;
            this->recvThread = nullptr;
        }
        return true;
    }

    bool ClientSocket::AsyncClose()
    {
        return this->AsyncClose(this->OnClosed,this->OnClosedParameter);
    }
    bool ClientSocket::AsyncClose(OnClosedCallback callback,void * parameter)
    {
        ClosedInArgs * args = new ClosedInArgs();
        args->Socket = this;
        args->Callback = callback;
        args->Parameter = parameter;
        return QueueUserWorkItem(AsyncClosedProc,args,WT_EXECUTEDEFAULT);
    }

    bool ClientSocket::Connect()
    {
        if(nullptr== this->handle)
        {
            return false;
        }
        return this->handle->Connect();
    }
    bool ClientSocket::Connect(const SOCKADDR * address)
    {
        if(nullptr== this->handle)
        {
            return false;
        }
        return this->handle->Connect(address);
    }

    bool ClientSocket::AsyncConnect()
    {
        return this->AsyncConnect(this->OnConnected);
    }
    bool ClientSocket::AsyncConnect(const SOCKADDR * address)
    {
        return this->AsyncConnect(address,this->OnConnected,this->OnConnectedParameter);
    }
    bool ClientSocket::AsyncConnect(const OnConnectedCallback callback,void * parameter)
    {
        return this->AsyncConnect(nullptr,callback,parameter);
    }
    bool ClientSocket::AsyncConnect(const SOCKADDR * address,const OnConnectedCallback callback,void * parameter)
    {
        ConnectInArgs * args = new ConnectInArgs();
        args->Socket = this;
        args->Callback = callback;
        args->Parameter =parameter;
        if(nullptr!= address)
        {
            args->Address = new SOCKADDR(*address);
        }
        return QueueUserWorkItem(AsyncConnectProc,args,WT_EXECUTEDEFAULT);
    }

    int ClientSocket::Send(const char * buffer,int length)
    {
        if(nullptr== this->handle)
        {
            return 0;
        }
        return this->handle->Send(buffer,length);
    }

    bool ClientSocket::AsyncSend(const char * buffer,int length)
    {
        return this->AsyncSend(buffer,length,this->OnSend,this->OnSendParameter);
    }
    bool ClientSocket::AsyncSend(const char * buffer,int length,const OnSendCallback callback,void * parameter)
    {
        SendInArgs * inArgs = new SendInArgs();
        inArgs->Socket = this;
        inArgs->Callback = callback;
        inArgs->Parameter = parameter;
        inArgs->Length = length;
        if(nullptr!= buffer)
        {
            inArgs->Buffer = new char[length];
            memset(inArgs->Buffer,0,length);
            memcpy(inArgs->Buffer,buffer,length);
        }
        return QueueUserWorkItem(AsyncSendProc,inArgs,WT_EXECUTEDEFAULT);
    }

    int ClientSocket::Recv(char* buffer,int length)
    {
        if(nullptr== this->handle)
        {
            return 0;
        }
        return this->handle->Recv(buffer,length);
    }

    bool ClientSocket::AsyncRecv(int length)
    {
        return this->AsyncRecv(length,this->OnRecv,this->OnRecvParameter);
    }
    bool ClientSocket::AsyncRecv(int length,const OnRecvCallback callback,void * parameter)
    {
        RecvInArgs * inArgs = new RecvInArgs();
        inArgs->Socket = this;
        inArgs->Callback = callback;
        inArgs->Parameter = parameter;
        inArgs->Length = length;
        return QueueUserWorkItem(AsyncRecvProc,inArgs,WT_EXECUTELONGFUNCTION);
    }

    bool ClientSocket::AsyncRepeatRecv(int length)
    {
        return this->AsyncRepeatRecv(length,this->OnRepeatRecv);
    }
    bool ClientSocket::AsyncRepeatRecv(int length,const OnRepeatRecvCallback callback,void * parameter)
    {
        if(nullptr == this->recvThread)
        {
            this->recvThread = new Thread();
        }
        if(this->recvThread->IsStarted())
        {
            return false;
        }
        RecvInArgs * inArgs = new RecvInArgs();
        inArgs->Socket = this;
        inArgs->Callback = callback;
        inArgs->Parameter = parameter;
        inArgs->Length = length;
        return this->recvThread->Start(AsyncRepeatRecvProc,inArgs);
    }

    int ClientSocket::SendTo(const char * buffer,int length)
    {
        if(nullptr== this->handle)
        {
            return 0;
        }
        return this->handle->SendTo(buffer,length);
    }
    int ClientSocket::SendTo(const SOCKADDR * address,const char * buffer,int length)
    {
        if(nullptr== this->handle)
        {
            return 0;
        }
        return this->handle->SendTo(address,buffer,length);
    }

    bool ClientSocket::AsyncSendTo(const char * buffer,int length)
    {
        return this->AsyncSendTo(buffer,length,this->OnSend);
    }
    bool ClientSocket::AsyncSendTo(const SOCKADDR * address,const char * buffer,int length)
    {
        return this->AsyncSendTo(address,buffer,length,this->OnSend);
    }
    bool ClientSocket::AsyncSendTo(const char * buffer,int length,const OnSendCallback callback,void * parameter)
    {
        SendInArgs * inArgs = new SendInArgs();
        inArgs->Socket = this;
        inArgs->Callback = callback;
        inArgs->Parameter = parameter;
        inArgs->Length = length;
        if(nullptr!= buffer)
        {
            inArgs->Buffer = new char[length];
            memset(inArgs->Buffer,0,length);
            memcpy(inArgs->Buffer,buffer,length);
        }
        return QueueUserWorkItem(AsyncSendToProc,inArgs,WT_EXECUTEDEFAULT);
    }
    bool ClientSocket::AsyncSendTo(const SOCKADDR * address,const char * buffer,int length, const OnSendCallback callback,void * parameter)
    {
        SendInArgs * inArgs = new SendInArgs();
        inArgs->Socket = this;
        inArgs->Callback = callback;
        inArgs->Parameter = parameter;
        inArgs->Length = length;
        if(nullptr!= buffer)
        {
            inArgs->Buffer = new char[length];
            memset(inArgs->Buffer,0,length);
            memcpy(inArgs->Buffer,buffer,length);
        }
        if(nullptr!= address)
        {
            inArgs->Address = new SOCKADDR(*address);
        }
        return QueueUserWorkItem(AsyncSendToProc,inArgs,WT_EXECUTEDEFAULT);
    }
}